package com.xingda.packing.dmds.domain;

import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.MathContext;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 产品包装箱
 */
@Data
@Slf4j
public class ProductBox implements Serializable {

    private static final long serialVersionUID = -5124480256518507960L;

    private String boxNo;

    /**
     * 每层要求装箱数量
     */
    private int floorLocationCount = 12;

    /**
     * 装箱要求
     */
    private PackDemand packDemand;

    /**
     * 工字轮装箱型号
     */
    private String wheelTag;

    private String wheelTagMap;

    private boolean customer;

    private String consumerName;

    /**
     * 位置集合
     */
    private List<BoxLocation> locationList = new ArrayList<>();

    /**
     * 要求装箱层数
     */
    private int requireFloorCount;

    /**
     * 要求装箱个数
     */
    private int requireCount;

    private int taskId;


    /**
     * 工字轮类型Id
     */
    private int wheelTypeId;

    /**
     * 1装箱任务 2 清线任务 3 半成品任务 4 不合格任务
     */
    private int type;

    /**
     * 允许装箱物料编号集合
     */
    private List<String> pkInvbasdocList;

    /**
     * 当前装箱层号(每层放过一次轮子之后才准)
     */
    @Setter(AccessLevel.NONE)
    private int currentPackFloorNo;

    @Getter(AccessLevel.NONE)
    @Setter(AccessLevel.NONE)
    private Wheel presetWheel;

    /**
     * 预放轮，用于检查预放的轮是否满足规则
     */
    public void prePutWheel(Wheel wheel) {
        if (floorWheelCount() < floorLocationCount) {
            this.presetWheel = wheel;
        } else {
            this.presetWheel = null;
        }
    }

    /**
     * 清除预放轮
     */
    public void clearPrePutWheel() {
        this.presetWheel = null;
    }

    /**
     * 放轮
     */
    public void putWheel(Wheel wheel) {
        BoxLocation boxLocation = new BoxLocation();
        int nextLocation = locationList.size() + 1;
        boxLocation.setLocationId(nextLocation);
        if (nextLocation % floorLocationCount == 0) {
            boxLocation.setFloor(nextLocation / floorLocationCount);
        } else {
            boxLocation.setFloor(nextLocation / floorLocationCount + 1);
        }
        boxLocation.setWheel(wheel);
        locationList.add(boxLocation);
        currentPackFloorNo = boxLocation.getFloor();
        this.presetWheel = null;
        log.info("开始放轮:{},{},{},{},{}", this.boxNo, wheel.getBarCode(), currentPackFloorNo, getCurrentPackFloorNo(), nextLocation);
        log.debug("-----装箱信息:{}-----", this.packDemand == null ? "ERROR---无装箱要求" : this.packDemand.getSurfaceLr());
        for (int i = 1; i <= getCurrentPackFloorNo(); i++) {
            int floor = i;
            String values = this.locationList.stream().filter(l -> l.getFloor() == floor)
                    .map(l -> l.getWheel().getTorsionValue().toString()).collect(Collectors.joining(","));
            log.info("码垛信息：层号{}，扭转值列表{}", floor, values);
        }
        log.info("box:{} 当前层:{},扭转值代数和：{}，均值：{}", this.boxNo, boxLocation.getFloor(), floorTorsionValue(), floorTorsionAvgValue());
        log.info("当前整箱方差：{},{}", boxNo, variance());
        log.debug("------------------");
        if (locationList.size() % floorLocationCount == 0) {
            // 已码数量为层数量的倍数，则进入到下一层（层号加一）
            currentPackFloorNo = boxLocation.getFloor() + 1;
        } else {
            currentPackFloorNo = boxLocation.getFloor();
        }
    }

    /**
     * 获得当前层号
     */
    public int getNowFloorNum() {
        int currentPackFloorCount = locationList.size();
        currentPackFloorCount++;
        int layer = currentPackFloorCount / floorLocationCount;
        // 当前层个数
        int currentFloorCount = currentPackFloorCount % floorLocationCount;
        if (currentFloorCount > 0) {
            layer++;
        }
        return layer;
    }

    /**
     * 获取当前层需要检查的工字轮 包括预放工字轮
     */
    public List<Wheel> floorCheckWheel() {
        List<Wheel> checkWheelList = locationList.stream().filter(location -> location.getFloor() == currentPackFloorNo)
                .map(BoxLocation::getWheel).filter(Objects::nonNull).collect(Collectors.toList());
        if (presetWheel != null) {
            checkWheelList.add(presetWheel);
        }
        return checkWheelList;
    }


    /**
     * 获得本层有重卷轮数
     */
    public int floorAnewWheelCount(int nowFloorNo) {
        return (int) locationList.stream().filter(location -> location.getFloor() == nowFloorNo)
                .map(BoxLocation::getWheel).filter(Objects::nonNull).filter(Wheel::isAnew).count();
    }

    public int floorSolderCount(int nowFloorNo) {
        return locationList.stream().filter(location -> location.getFloor() == nowFloorNo)
                .map(BoxLocation::getWheel).filter(Objects::nonNull).map(Wheel::getSolderJointCount).reduce(0, Integer::sum);
    }


    /**
     * 获取需要检查的工字轮 包括预放工字轮
     */
    public List<Wheel> allCheckWheel() {
        List<Wheel> checkWheelList = locationList.stream().map(BoxLocation::getWheel).filter(Objects::nonNull)
                .collect(Collectors.toList());
        if (presetWheel != null) {
            checkWheelList.add(presetWheel);
        }
        return checkWheelList;
    }

    /**
     * 获取整箱焊点数
     */
    public int solderJointCount() {
        return allCheckWheel().stream().map(Wheel::getSolderJointCount).reduce(0, Integer::sum);
    }

    /**
     * 本层重卷轮是否大于等于要求的最小值,大于等于返回true
     */
    public boolean isFloorAnewGreaterThanMin() {
        return floorAnewWheelCount(currentPackFloorNo) >= getPackDemand().getFloorSolderWheelNum()
                .getFloorSolderWheelNum(getNowFloorNum()).getMin().intValue();
    }

    public boolean isFloorSolderGreaterThanMin() {
        return floorSolderCount(currentPackFloorNo) >= getPackDemand().getFloorSolderNum()
                .getFloorSolderNum(getNowFloorNum()).getMin().intValue();
    }

    /**
     * 本层重卷轮限制是否启用
     */
    public boolean isFloorAnewEnable() {
        return !getPackDemand().getFloorSolderWheelNum().getFloorSolderWheelNum(getNowFloorNum()).isEnable();
    }

    public boolean isFloorAnewNumEnable() {
        return !getPackDemand().getFloorSolderNum().getFloorSolderNum(getNowFloorNum()).isEnable();
    }

    /**
     * 本层重卷轮是否小于要求的最大值,小于返回true
     */
    public boolean isFloorAnewLessThanMax() {
        return floorAnewWheelCount(currentPackFloorNo) < getPackDemand().getFloorSolderWheelNum()
                .getFloorSolderWheelNum(getNowFloorNum()).getMax().intValue();

    }

    public boolean isFloorSolderLessThanMax() {
        return floorSolderCount(currentPackFloorNo) <= getPackDemand().getFloorSolderNum()
                .getFloorSolderNum(getNowFloorNum()).getMax().intValue();

    }

    public boolean isFloorSolderLessThanMaxIncludeCheck() {
        return floorCheckWheel().stream().map(Wheel::getSolderJointCount).reduce(0, Integer::sum) <= getPackDemand().getFloorSolderNum()
                .getFloorSolderNum(getNowFloorNum()).getMax().intValue();
    }


    /**
     * 获取该层已码垛数量
     */
    public int floorWheelCount() {
        return (int) locationList.stream().filter(location -> location.getFloor() == currentPackFloorNo).count();
    }

    /**
     * 获取当前层扭转值（代数和）
     */
    public BigDecimal floorTorsionValue() {
        return floorCheckWheel().stream().map(Wheel::getTorsionValue).filter(Objects::nonNull).reduce(BigDecimal.ZERO,
                BigDecimal::add);
    }

    /**
     * 获取当前层工字轮扭转平均值
     */
    public BigDecimal floorTorsionAvgValue() {
        if (CollectionUtils.isEmpty(locationList)) {
            return BigDecimal.ZERO;
        }
        int floorWheelCount = floorWheelCount();
        if (floorWheelCount == 0) {
            return BigDecimal.ZERO;
        }
        return locationList.stream().filter(location -> location.getFloor() == currentPackFloorNo)
                .filter(location -> Objects.nonNull(location.getWheel()))
                .map(location -> location.getWheel().getTorsionValue()).filter(Objects::nonNull)
                .reduce(BigDecimal.ZERO, BigDecimal::add)
                .divide(new BigDecimal(floorWheelCount), MathContext.DECIMAL64);
    }

    /**
     * 本层扭转和
     */
    public double floorTorsionSum() {
        return floorCheckWheel().stream().map(Wheel::getTorsionValue).mapToDouble(BigDecimal::doubleValue).sum();

    }

    /**
     * 本层扭转和是否在要求范围内 是返回true
     */
    public boolean isFloorTorsionRange() {
        double r_min = getPackDemand().getFloorTorsion().getMin().doubleValue();
        double r_max = getPackDemand().getFloorTorsion().getMax().doubleValue();
        return floorTorsionSum() >= r_min && floorTorsionSum() <= r_max;
    }

    /**
     * 标准差在是否在要求范围内 是返回true
     */
    public boolean isBoxVarianceRange() {
        double r_max = getPackDemand().getBoxDeviation().getMax().doubleValue();
        return variance().doubleValue() <= r_max;
    }


    /**
     * 获取当前层机台数量，排除重卷轮机台
     */
    public Map<String, Integer> floorSameMachineNum() {
        Map<String, Integer> machineMap = new HashMap<>();
        floorCheckWheel().stream().filter(Wheel::isNotAnew).map(Wheel::getMachineNo)
                .collect(Collectors.groupingBy(m -> m)).forEach((k, v) -> machineMap.put(k, v.size()));
        return machineMap;
    }

    /**
     * 获取产品标准方差
     */
    public BigDecimal variance() {
        List<BigDecimal> list = allCheckWheel().stream().map(Wheel::getTorsionValue).filter(Objects::nonNull)
                .collect(Collectors.toList());
        return calcVariance(list);
    }

    /**
     * 是否已装满
     */
    public boolean isFull() {
        return locationList.size() >= requireFloorCount * floorLocationCount;
    }

    /**
     * 计算标准方差
     */
    public BigDecimal calcVariance(List<BigDecimal> list) {
        if (list.size() <= 1) {
            return BigDecimal.ZERO;
        }
        BigDecimal avg = list.stream().reduce(BigDecimal.ZERO, BigDecimal::add).divide(new BigDecimal(list.size()),
                MathContext.DECIMAL64);
        return BigDecimal.valueOf(
                Math.sqrt(list.stream().map(l -> (l.subtract(avg).pow(2))).reduce(BigDecimal.ZERO, BigDecimal::add)
                        .divide(new BigDecimal(list.size() - 1), MathContext.DECIMAL64).doubleValue()));
    }

    public double floorPowSum() {
        List<BigDecimal> list = allCheckWheel().stream().map(Wheel::getTorsionValue).filter(Objects::nonNull)
                .collect(Collectors.toList());
        return calcPowSum(list);
    }

    private double calcPowSum(List<BigDecimal> list) {
        PackDemandRange floorTorsion = getPackDemand().getFloorTorsion();
        BigDecimal asAvg = floorTorsion.getMin().add(floorTorsion.getMax()).divide(new BigDecimal("2"),MathContext.DECIMAL64)
                .divide(new BigDecimal(String.valueOf(getFloorLocationCount())), MathContext.DECIMAL64);
        return list.stream().map(l -> (l.subtract(asAvg).pow(2))).reduce(BigDecimal.ZERO, BigDecimal::add)
                .doubleValue();
    }

}
